package top.vanchor.myblog2back.utils;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Ding
 * @create 2021/11/10 13:25
 */
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.vanchor.myblog2back.config.JwtConfigProperties;
import top.vanchor.myblog2back.entity.BackAuthGroupRule;
import top.vanchor.myblog2back.entity.BackAuthRule;
import top.vanchor.myblog2back.service.IBackAdminService;
import top.vanchor.myblog2back.service.IBackAuthGroupRuleService;
import top.vanchor.myblog2back.service.IBackAuthGroupService;
import top.vanchor.myblog2back.service.IBackAuthRuleService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;

@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {

    private static final ThreadLocal<String> info = new ThreadLocal<>();

    @Autowired
    private JwtConfigProperties jwtConfigProperties;

    private static IBackAdminService adminService;

    private static IBackAuthGroupService groupService;

    private static IBackAuthGroupRuleService groupRuleService;

    private static IBackAuthRuleService ruleService;

    @Resource
    public void setAdminService(IBackAdminService adminService) {
        JwtInterceptor.adminService = adminService;
    }

    @Resource
    public void setGroupService(IBackAuthGroupService groupService) {
        JwtInterceptor.groupService = groupService;
    }

    @Resource
    public void setGroupRuleService(IBackAuthGroupRuleService groupRuleService) {
        JwtInterceptor.groupRuleService = groupRuleService;
    }

    @Resource
    public void setRuleService(IBackAuthRuleService ruleService) {
        JwtInterceptor.ruleService = ruleService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 这里是个坑，因为带请求带headers时，ajax会发送两次请求，
        // 第一次会发送OPTIONS请求，第二次才会发生get/post请求，所以要放行OPTIONS请求
        // 如果是OPTIONS请求，让其响应一个 200状态码，说明可以正常访问
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            // 放行OPTIONS请求
            return true;
        }

        // JwtConfigProperties通过@Autowired的值为null，所以需要通过以下方法获取bean
        if(jwtConfigProperties == null){
            BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
            jwtConfigProperties = (JwtConfigProperties) factory.getBean("jwtConfigProperties");
        }
        boolean verify = false;
        String errMsg = "";

        // 获取header中的token --可能为空
        String tokenTemp = request.getHeader("Authorization");
        // 如果header中不存在token，则给予null，不做拆分，否则尝试拆分Bearer前缀
        String token = StringUtils.isEmpty(tokenTemp) ? null : tokenTemp.replace("Bearer ", "");

        try {
            verify = JWTUtils.verify(token, jwtConfigProperties);
        } catch (Exception e) {
            errMsg = e.getMessage();
            e.printStackTrace();
        }
        // 验证token，如果验证失败就重定向到未登录页面
        if (!verify){
            errMsg = URLEncoder.encode(errMsg, "UTF-8");
            //这里是个坑，在重定向这里需要设置跨域，不然vue前端会报跨域问题
            response.setHeader("Access-Control-Allow-Origin", "*");
            /**
             * 根据请求url进行判断反馈重定向到提示页面
             */
            if ("/user/get".equals(request.getRequestURI())){
                response.sendRedirect("/user/unlogin?msg=" + errMsg);
            }else{
                response.sendRedirect("/user/unlogin?msg=" + errMsg);
            }
            return false;
        }
        info.set(token);
        /**
         * 上面的都通过了，但是还需要进一步权限判断
         */
        //非后台不走拦截
        if (!request.getRequestURI().startsWith("/admin")){
            return true;
        }
        if (!isPass(request.getRequestURI())) {
            errMsg = URLEncoder.encode("无权访问接口："+request.getRequestURI(), "UTF-8");
            //这里是个坑，在重定向这里需要设置跨域，不然vue前端会报跨域问题
            response.setHeader("Access-Control-Allow-Origin", "*");
            if ("/user/get".equals(request.getRequestURI())) {
                response.sendRedirect("/user/unlogin?msg=" + errMsg);
            } else {
                response.sendRedirect("/user/unlogin?msg=" + errMsg);
            }
            return false;
        }
        return true;
    }

    public boolean isPass(String requestURI) {
        Integer aid = Integer.parseInt(Objects.requireNonNull(JWTUtils.getUserId(JwtInterceptor.getTokenInfo())));
        Integer gid = adminService.getById(aid).getAGroupId();
        boolean isSuperAdmin = "SUPER_ADMIN".equals(groupService.getGroupById(gid).getRole());
        /*
            不需要权限拦截的接口
            加在这里可以直接跳过
         */
        if ("/admin/adminUser/getUserInfo".equals(requestURI)
                || "/admin/adminUser/getRuleRouters".equals(requestURI)
                || "/admin/adminUser/logout".equals(requestURI)
                || "/upload/image".equals(requestURI)
        ) {
            return true;
        }
        /*
            需要拦截的接口
         */
        //最高权限，只有超级管理员可以访问（特殊接口，不允许任何普通管理员访问）
        if (
                (requestURI.startsWith("/admin/adminUser") ||
                        requestURI.startsWith("/admin/adminGroup") ||
                        requestURI.startsWith("/admin/adminRole")
                )) {
            return isSuperAdmin;
        } else {
            //普通权限拦截(超级管理无视)
            if (!isSuperAdmin) {
                boolean isOk = false;
                List<BackAuthGroupRule> groupRules = groupRuleService.getIdsByGroupId(gid);
                List<BackAuthRule> rules = ruleService.getAllRulesNoTree(groupRules, "ADMIN");
                for (BackAuthRule rule : rules) {
                    System.err.println("请求接口："+requestURI + "  遍历授权接口：" + rule.getPath());
                    if (("/".equals(requestURI) && "/".equals(rule.getPath())) || (requestURI.startsWith(rule.getPath()) && !"/".equals(rule.getPath()))) {
                        isOk = true;
                        System.err.println("有权访问接口："+requestURI + "  通行接口：" + rule.getPath());
                        break;
                    }
                }
                return isOk;
            }
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //程序运行结束之后，删除线程
        info.remove();
    }

    public static String getTokenInfo() {
        return info.get();
    }
}